
import random

from Chromosome import Chromosome
import Define

class Environment:

    def __init__(self):
        '''
        population          种群：用于保存染色体
        bestChromosome      最佳染色体：保存当代最佳染色体
        bestfitness         最佳染色体的适应度：保证当代最佳染色体的适应度
        homeoticRate        连接基因突变率
        mutationRate        突变
        ISTranspositionRate         IS
        RISTranspositionRate        RIS
        geneTranspositionRate       基因转座
        onePointRecombinationRate   单点重组
        twoPointRecombinationRate   两点重组
        geneRecombination           基因重组
        '''

        self.population = []
        self.bestChromosome=[]
        self.bestfitness=[]
        self.homeoticRate = 0
        self.mutationRate = 0
        self.ISTranspositionRate = 0
        self.ISElementLength = 0
        self.RISTranspositionRate = 0
        self.RISElementLength = 0
        self.geneTranspositionRate = 0
        self.onePointRecombinationRate = 0
        self.twoPointRecombinationRate = 0
        self.geneRecombinationRate = 0
        self.randomSetRate = 0
        self.DcISTranspositionRate = 0
        self.DcISElementLength = 0
        self.numRandom = 0
        self.RandomRangeStart = 0
        self.RandomRangeEnd = 0
        self.RandomPRECISION = 0

        self.neutral_flag = False
        self.TriggerFitnessCount=20
        self.neutralValue=0


    def setRates(self, homeoticRate=0, mutationRate=0,ISTranspositionRate=0,ISElementLength=[], RISTranspositionRate=0,RISElementLength=[], geneTranspositionRate=0,
                 onePointRecombinationRate=0, twoPointRecombinationRate=0, geneRecombinationRate=0,randomSetRate=0,DcISTranspositionRate=0,DcISElementLength=[],numRandom=0,
                 RandomRangeStart=-1,RandomRangeEnd=1,RandomPRECISION=2,TriggerFitnessCount=20,neutralValue=0):
        '''
        homeoticRate        连接基因突变率
        mutationRate        突变
        ISTranspositionRate         IS
        RISTranspositionRate        RIS
        geneTranspositionRate       基因转座
        onePointRecombinationRate   单点重组
        twoPointRecombinationRate   两点重组
        geneRecombination           基因重组
        '''
        self.homeoticRate = homeoticRate
        self.mutationRate = mutationRate
        self.ISTranspositionRate = ISTranspositionRate
        self.ISElementLength = ISElementLength
        self.RISTranspositionRate = RISTranspositionRate
        self.RISElementLength = RISElementLength
        self.geneTranspositionRate = geneTranspositionRate
        self.onePointRecombinationRate = onePointRecombinationRate
        self.twoPointRecombinationRate = twoPointRecombinationRate
        self.geneRecombinationRate = geneRecombinationRate
        self.randomSetRate=randomSetRate
        self.DcISTranspositionRate=DcISTranspositionRate
        self.DcISElementLength=DcISElementLength
        self.numRandom=numRandom
        self.RandomRangeStart=RandomRangeStart
        self.RandomRangeEnd=RandomRangeEnd
        self.RandomPRECISION=RandomPRECISION

        self.TriggerFitnessCount=TriggerFitnessCount
        self.neutralValue=neutralValue


    def init(self, populationSize, numGenes, numHomeotics, headLength, homeoticHeadLength, genome, link_genome,
             homeotic_flag=False,DC_flag=False,neutral_flag=False):
        '''
        初始化种群
        populationSize      种群大小
        numGenes            染色体基因个数
        numHomeotics        染色体连接基因个数
        headLength          基因头部长度
        homeoticHeadLength  连接基因头部长度
        genome              基因 Genome类
        link_genome         连接 link_genome 类
        '''
        self.neutral_flag=neutral_flag


        self.population = [Chromosome(homeotic_flag,DC_flag,genome,link_genome).initRand(numGenes, numHomeotics, headLength, homeoticHeadLength
                                                                      , self.numRandom, self.RandomRangeStart,self.RandomRangeEnd,self.RandomPRECISION) for i in range(populationSize)]
    def run(self, inputsOutputs, evalFunction,generationCount,M,DataCount,absoluteError_flag,roulette_flag):
        '''
        :param inputsOutputs:   保存输入与输出集合
        :param evalFunction:    适应度函数
        :param generationCount: 执行代数
        :return:
        generation:             表示第几代种群
        i_chromosome            用于保存当代种群第i条染色体
        C_valueList             表示染色体,样本输出值
        fitness                 保存当前染色体的适应度
        maxFitness_flag=True    为True,返回的maxFitness:M*DataCount
        '''

        ResultBestFitness=float("-inf")
        ResultBestChromosome=[]

        generation = 0
        AccFitnessCount=0
        PreBestFitness=0

        # # 下面这四行到时候写正式代码要删掉
        # plt.rcParams['font.sans-serif'] = ['SimHei']  # 添加这条可以让图形显示中文
        # x_axis_data = []
        # y_axis_data = []
        # y_predict = []


        while True:
            sum_fitness=0
            NowBestFitness=float("-inf")
            NowBestChromosome=[]
            #进行种群打印
            # self.printChromosomes(generation)
            generation += 1

            for i in range(len(self.population)):
                C_valueList = []
                i_chromosome = self.population[i]

                #计算每个染色体对应的 C_value
                # print(inputsOutputs[0])
                for inputs in inputsOutputs[0]:
                    C_vlaue=i_chromosome.eval(inputs)
                    C_valueList.append(C_vlaue)
                #evalFunction 进行适应度评估
                fitness = evalFunction(C_valueList, inputsOutputs[1],maxFitness_flag=False, M=M, DataCount=DataCount, absoluteError_flag=absoluteError_flag)
                # print("fitness:",fitness)
                sum_fitness=sum_fitness+fitness
                #保存当代种群最佳适应度的值,与对应的染色体
                if fitness > NowBestFitness:
                    NowBestFitness = fitness
                    NowBestChromosome=i_chromosome

                # 完美解停止执行
                if fitness == evalFunction([], [], maxFitness_flag=True,M=M,DataCount=DataCount):
                    # print("*" * 46, "  完美解  ", "*" * 46)
                    # print("第",generation,"代","BestFitness=", fitness)
                    # i_chromosome.printChromosome()
                    # i_chromosome.simplifyChromosome()

                    # # 下面这段要删掉
                    # # 画迭代次数和最佳染色体适应度的关系图
                    # # plot中参数的含义分别是横轴值，纵轴值，线的形状，颜色，透明度,线的宽度和标签
                    # plt.plot(x_axis_data, y_axis_data, 'ro-', color='#FF0000', alpha=0.8, linewidth=1)
                    # plt.plot(x_axis_data, y_predict, 'ro-', color='#0000FF', alpha=0.8, linewidth=1)
                    # # 显示标签，如果不加这句，即使在plot中加了label='一些数字'的参数，最终还是不会显示标签
                    # plt.legend(loc="upper right")
                    # plt.xlabel('种群代数')
                    # plt.ylabel('最佳染色体适应度')
                    # plt.show()


                    return i_chromosome

                self.population[i] = (i_chromosome, fitness)

            # 打印当代最佳染色体
            # print("第",generation,"代，BestFitness=",NowBestFitness)
            # NowBestChromosome.printChromosome()
            # NowBestChromosome.simplifyChromosome()
            # print("*"*46)

            # 保存最终最佳解，与对应的染色体
            if ResultBestFitness<NowBestFitness:
                ResultBestFitness=NowBestFitness
                ResultBestChromosome=NowBestChromosome



            # # 下面这两行要删掉
            # x_axis_data.append(generation)
            # y_axis_data.append(NowBestFitness)
            # y_predict.append(ResultBestFitness)



            # 繁衍代数大于generationCount退出, 并输出最佳解
            if generation >= generationCount:
                # print("*"*46,"  ",generationCount,"代后最佳解  ","*"*46)
                # print("result_fitness=  ", ResultBestFitness)
                # ResultBestChromosome.printChromosome()
                # ResultBestChromosome.simplifyChromosome()

                # # 下面这段要删掉
                # # 画迭代次数和最佳染色体适应度的关系图
                # # plot中参数的含义分别是横轴值，纵轴值，线的形状，颜色，透明度,线的宽度和标签
                # plt.plot(x_axis_data, y_axis_data, 'ro-', color='#FF0000', alpha=0.8, linewidth=1)
                # plt.plot(x_axis_data, y_predict, 'ro-', color='#0000FF', alpha=0.8, linewidth=1)
                # # 显示标签，如果不加这句，即使在plot中加了label='一些数字'的参数，最终还是不会显示标签
                # plt.legend(loc="upper right")
                # plt.xlabel('种群代数')
                # plt.ylabel('最佳染色体适应度')
                # plt.show()


                # return ResultBestFitness, ResultBestChromosome
                return ResultBestChromosome

            # 赌盘选择，复制，修饰
            selected=self.select(sum_fitness,NowBestChromosome,roulette_flag)
            self.replicate(selected)

            # 产生中性基因
            if self.neutral_flag:
                if(NowBestFitness>PreBestFitness*0.90 and NowBestFitness<PreBestFitness*1.10):
                    AccFitnessCount += 1
                    # print("ACC:",AccFitnessCount,"  LenGenes:",len(self.population[0].genes))
                    if AccFitnessCount >=self.TriggerFitnessCount:
                        self.addNeutralGene(inputsOutputs,self.neutralValue)
                        AccFitnessCount=0
                else:
                    AccFitnessCount = 0
            PreBestFitness=NowBestFitness
            self.modify()

    def select(self, sum_fitness,NowBestChromosome,roulette_flag=True):

        '''
        :param sum_fitness:         总的适应度
        :param NowBestChromosome:   用于保存当代最佳染色体
        :return: selected           用于保存赌盘选择出的染色体
        :self.population[i] = (i_chromosome, fitness) 其中保存了染色体的适应度
        '''
        random.seed(Define.SEED)
        Define.SEED+=1

        if roulette_flag:
            accumulator = 0  # 设置累加器
            roulette=[]     # 设置轮盘
            selected=[]

            #制作赌盘，如果percentage  区间为0的
            for i in range(len(self.population)):
                percentage = self.population[i][1] / sum_fitness * 10
                if percentage != 0:
                    roulette.append(percentage + accumulator)
                    accumulator = accumulator + percentage
                else:
                    roulette.append(0)
            #一共选择 len(self.population)-1 条染色体体，最后一条保存最佳染色体。
            for i in range(len(self.population)-1):
                #产生随机数
                ranNumber=random.uniform(0,10)
                #进行选择染色体
                for i_pos in range(len(self.population)):
                    if ranNumber <= roulette[i_pos]:
                        selected.append(self.population[i_pos][0])
                        break
            #最后一条保存最佳染色体
            selected.append(NowBestChromosome)
            return  selected
        else:
            selected=[]
            for i in range(len(self.population)-1):
                s1=random.randint(0,len(self.population)-1)
                s2=random.randint(0,len(self.population)-1)
                while(s1 == s2):
                    s2=random.randint(0,len(self.population)-1)
                if(self.population[s1][1] < self.population[s2][1]):
                    selected.append(self.population[s2][0])
                else:
                    selected.append(self.population[s1][0])
            selected.append(NowBestChromosome)
            return  selected

    def replicate(self, chromosomes):
        '''
        进行染色体复制，保证更新在 self.population
        :param chromosomes:       赌盘后选择种群染色体
        :return:
        '''
        self.population = []
        for chromosome in chromosomes:
            self.population.append(chromosome.replicate())

    def modify(self):

        '''
        染色体修饰
        mutation:           突变
        ISTransposition     IS转座
        RISTransposition    RIS转座
        geneTransposition   基因转座
        onePointRecombination   单点重组
        twoPointRecombination   两点重组
        geneRecombination       基因重组
        :return:
        '''
        random.seed(Define.SEED)
        Define.SEED+=1
        for i in range(len(self.population)):

            chromosome = self.population[i]

            chromosome.mutation(self.mutationRate, self.homeoticRate,self.randomSetRate,self.RandomRangeStart,
                                self.RandomRangeEnd,self.RandomPRECISION)
            chromosome.ISTransposition(self.ISTranspositionRate, self.homeoticRate,self.ISElementLength,
                                       self.DcISTranspositionRate,self.DcISElementLength)

            chromosome.RISTransposition(self.RISTranspositionRate, self.homeoticRate,self.RISElementLength)

            chromosome.geneTransposition(self.geneTranspositionRate, self.homeoticRate)

            #选择于i不同的染色体的，otherIndex
            otherIndex = i
            while otherIndex == i:
                otherIndex = random.randint(0, len(self.population) - 1)
            chromosome.onePointRecombination(self.onePointRecombinationRate, self.homeoticRate, self.population[otherIndex])

            otherIndex = i
            while otherIndex == i:
                otherIndex = random.randint(0, len(self.population) - 1)
            chromosome.twoPointRecombination(self.twoPointRecombinationRate, self.homeoticRate, self.population[otherIndex])

            otherIndex = i
            while otherIndex == i:
                otherIndex = random.randint(0, len(self.population) - 1)
            chromosome.geneRecombination(self.geneRecombinationRate, self.homeoticRate, self.population[otherIndex])

    def addNeutralGene(self,inputsOutputs,neutralValue):
        chromosome=self.population[0]
        neutralGene=chromosome.createNeutralGene(inputsOutputs,neutralValue,numRandom=self.numRandom,
                                     RandomRangeStart=self.RandomRangeStart,RandomRangeEnd=self.RandomRangeEnd,
                                     RandomPRECISION=self.RandomPRECISION)
        for chromosome in self.population:
            chromosome.updateNeutralGene(neutralGene=neutralGene)

    def printChromosomes(self, generation):
        '''
        打印染色体
        :param generation:   迭代的代数
        :return: None
        '''
        # print("generation: ",generation)
        for Chromosome in self.population:
            Chromosome.printChromosome()

